#include <iostream>
#include <queue>
#include <pthread.h>
using namespace std;

template<class T>
class BlockingQueue
{
public:
    const static int _maxcap = 10;

    BlockingQueue(int maxcap = _maxcap) :_qmax_cap(maxcap)
    {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&_ccond,nullptr);
        pthread_cond_init(&_pcond,nullptr);
    }

    ~BlockingQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_ccond);
        pthread_cond_destroy(&_pcond);
    }

    void push(T& data)
    {
        pthread_mutex_lock(&_mutex);
        
        //细节2:判断条件不能用if，而要用while
        //原因:当有多个生产者线程时，并且此时消费者调用的是pthread_cond_boardcast来唤醒所有线程
        //恰好此时队列中只有一个空位置
        //这种情况下，会造成多余线程的伪唤醒，必须使用while二次判断条件是否满足
        //唤醒所有线程后，这些线程会先竞争同一个锁，第一个获取锁的线程会push进一个数据，导致队列为满，然后再解锁
        //下一个竞争到锁的线程，再次进行条件判断，此时队列为满，该线程就会挂起等待
        while(is_full())
        {
            //队列已满，生产条件不满足，将生产者线程挂起
            //细节1:pthread_cond_wait的第二个参数，必须是我们正在使用的互斥锁
            //a.pthread_cond_wait：该函数调用时，会原子性的方式，将锁释放，将自己挂起
            //b.pthread_cond_wait: 该函数被唤醒返回时，会自动传回原先的互斥锁
            pthread_cond_wait(&_pcond,&_mutex); 
            //如果有多个生产者线程，这个锁可能会被其他生产者线程获取，使得其他生产者线程同样由于条件变量挂起等待在这里
        }

        //生产条件满足，进行生产
        _q.push(data);
        
        //此时有商品，可以被消费，唤醒消费者线程
        //pthread_cond_signal(&_ccond);
        pthread_mutex_unlock(&_mutex);
        //细节3:信号可以在解锁后进行，也可以在解锁之前进行
        pthread_cond_signal(&_ccond); //发送信号也是希望线程能重新获得锁
    }

    void pop(T* data)
    {
        pthread_mutex_lock(&_mutex);
        while(is_empty())
        {
            //队列为空，无法进行消费，将消费者挂起等待
            pthread_cond_wait(&_ccond,&_mutex);
        }
        
        //消费条件满足
        *data = _q.front();
        _q.pop();

        //此时有商品被消费了，队列空出位置，唤醒生产者继续生产
        pthread_cond_signal(&_pcond);
        pthread_mutex_unlock(&_mutex);
    }

private:
    bool is_full()
    {
        return _q.size() == _qmax_cap;
    }

    bool is_empty()
    {
        return _q.empty();
    }

private:
    queue<T> _q;
    pthread_mutex_t _mutex;//生产者消费者共用一把锁
    pthread_cond_t _ccond;//消费者条件变量
    pthread_cond_t _pcond;//生产者条件变量
    int _qmax_cap;//队列容量最大值
};
